package com.lry.basic.jvm.asm.transform_class.demo11;

import aj.org.objectweb.asm.ClassVisitor;
import aj.org.objectweb.asm.FieldVisitor;
import aj.org.objectweb.asm.MethodVisitor;
import aj.org.objectweb.asm.Opcodes;

import static jdk.internal.org.objectweb.asm.Opcodes.*;

/**
 * 给方法计时
 */

public class MethodTimerVisitor extends ClassVisitor {
    private String owner;
    private boolean isInterface;

    public MethodTimerVisitor(int api, ClassVisitor cv) {
        super(api,cv);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        this.owner = name;
        this.isInterface = (access & ACC_INTERFACE) !=0;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String sig, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, sig, exceptions);
        if(!isInterface&&mv!=null&& !"<init>".equals(name)&&!"main".equals(name)&&!"<clinit>".equals(name)){
            boolean isAbstract = (access&Opcodes.ACC_ABSTRACT)==Opcodes.ACC_ABSTRACT;
            boolean isNative = (access&Opcodes.ACC_NATIVE)== Opcodes.ACC_NATIVE;
            if(!isAbstract&&!isNative){
                mv = new MethodTimerAdapter(api, mv,owner);
            }
        }
        return mv;
    }

    //给类加一个静态属性
    @Override
    public void visitEnd() {
        if(!isInterface){

            FieldVisitor fv = super.visitField(ACC_PUBLIC|ACC_STATIC,"timer","J",null,null);
            if(fv!=null){
                fv.visitEnd();
            }
        }
        super.visitEnd();
    }

    private static class MethodTimerAdapter extends MethodVisitor{
        private final String owner;
        public MethodTimerAdapter(int api, MethodVisitor methodVisitor,String owner) {
            super(api, methodVisitor);
            this.owner = owner;
        }

        @Override
        public void visitCode() {
            //打印时间戳
            super.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
            super.visitMethodInsn(INVOKESTATIC,"java/lang/System", "currentTimeMillis", "()J", false);
            super.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream", "println", "(J)V", false);

            //timer = timer-当前时间
            super.visitFieldInsn(Opcodes.GETSTATIC, owner, "timer", "J");
            super.visitMethodInsn(INVOKESTATIC,"java/lang/System", "currentTimeMillis", "()J", false);
            super.visitInsn(LSUB);
            super.visitFieldInsn(Opcodes.PUTSTATIC, owner, "timer", "J");

            super.visitCode();
        }

        @Override
        public void visitInsn(int opcode) {
            if (opcode == Opcodes.ATHROW || (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
                //打印时间戳
                super.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
                super.visitMethodInsn(INVOKESTATIC,"java/lang/System", "currentTimeMillis", "()J", false);
                super.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream", "println", "(J)V", false);


                super.visitFieldInsn(GETSTATIC, owner, "timer", "J");
                super.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
                super.visitInsn(LADD);
                super.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
            }
            super.visitInsn(opcode);
        }
    }
}
